Open In Colab

Prueba técnica: Análisis de mercado mobiliario

Desarrollado por Diego Lesmes

1. Prensado como un Data Science

¿Qué datos crees que te ayudarían a trabajar en el problema? ¿Por qué?

Respuesta

Dentro de los diferentes atributos o características que podrían ser útiles para determinar el valor de una propiedad, se pueden tener en cuenta características como:

  • Tipo de propiedad: Residencial, comercial, apartamento entre otros
  • Área: Tamaño del predio que ocupa la propiedad
  • Área cubierta: Área Total sin tener en cuenta área al expuesta al aire libre
  • Ubicación: datos de latitud y longitud, para deerminar la ubicación del predio, de aca podemos obtener otros datos como:
    • Departamento
    • Ciudad
    • Barrio
  • Diseño arquitectónico: tipo Arquitectonico de la propiedad
  • Año de construcción: Esta fecha nos podra indicar la antiguedad de la propiedad
  • '# de balcones: La cantidad de balcones con los que cuenta la propiedad
  • '# de habitaciones: La cantidad de cuartos con la que cuenta la propiedad
  • '# de baños: La cantidad de baños con la que cuenta la propiedad
  • Precio Comercial: El precio al cual esta valuada la propiedad
  • Precio catastral: El precio usado para causar impuestos
  • Precio de venta: El precio de venta de la propiedad
  • Precio por metro cuadrado: El valor por unidad de área d ela propiedad, es un predictor muy preciso al momento de realizar la valuación
  • Fecha de la venta: Si la propiedad fue vendida anteriormente de esta manera se podría aplicarle el efecto de la infliación a los precios para hacerlos comparables
  • Paqueadero: Si la propiedad cuenta con parqueadero
  • Garaje: Si cuenta con un salón de garaje
  • Unidades residenciales: Si la propiedad cuenta con varios apartamentos
  • Piso: si se trata de una unidad residencial en que piso se encuentra

En general esta información podría ser muy util para poder realizar un modelo predictivo que determine el valor de la propiedad, debido a que estas características inciden principalmente a la hora de realizar la venta de las propiedades, sin embargo el modelo esta en función del procedimiento que se realice en la información disponible y el análisis estadístico pertinente.

2. Análisis Exploratorio de Datos

Importamos el data set

from google.colab import files data_to_load = files.upload()

In [2]:
pip install folium
Collecting folium
  Using cached folium-0.11.0-py2.py3-none-any.whl (93 kB)
Collecting branca>=0.3.0
  Using cached branca-0.4.1-py3-none-any.whl (24 kB)
Requirement already satisfied: requests in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from folium) (2.24.0)
Requirement already satisfied: numpy in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from folium) (1.18.5)
Requirement already satisfied: jinja2>=2.9 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from folium) (2.11.2)
Requirement already satisfied: idna<3,>=2.5 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from requests->folium) (2.10)
Requirement already satisfied: chardet<4,>=3.0.2 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from requests->folium) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from requests->folium) (2020.6.20)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from requests->folium) (1.25.9)
Requirement already satisfied: MarkupSafe>=0.23 in /home/asf/anaconda3/envs/DS4A_Gral/lib/python3.8/site-packages (from jinja2>=2.9->folium) (1.1.1)
Installing collected packages: branca, folium
Successfully installed branca-0.4.1 folium-0.11.0
Note: you may need to restart the kernel to use updated packages.
In [1]:
import io
import pandas as pd
import seaborn as sns
sns.set_style('darkgrid')
import pandas.util.testing as tm
import numpy as np
import matplotlib.pyplot as plt


from scipy import stats
import statsmodels.api as sm
import statsmodels.formula.api as smf
from sklearn import tree
from sklearn.tree import export_graphviz
from sklearn.tree import DecisionTreeClassifier
from sklearn import metrics
from sklearn import ensemble
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_curve, auc, accuracy_score
from sklearn.model_selection import StratifiedKFold, train_test_split
from sklearn import neighbors

import folium  #needed for interactive map
from folium.plugins import HeatMap

import wordcloud as wordcloud
from wordcloud import WordCloud

def RMSE(prediction,true_values):  # Root Mean Squared Error
    return np.sqrt(np.mean(np.square(prediction-true_values)))
def MAE(prediction,true_values): #Mean Absolute Error
    return np.mean(np.abs(prediction-true_values))
def MAPE(prediction,true_value): #Mean Absolute Error Percenage
    return np.mean(np.abs((prediction-true_value)/true_value)*100)
<ipython-input-1-eca4765734a9>:5: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
In [2]:
df = pd.read_csv('DS_Proyecto_01_Datos_Properati.csv')
df.head()
Out[2]:
start_date end_date created_on lat lon l1 l2 l3 rooms bedrooms bathrooms surface_total surface_covered price currency title description property_type operation_type
0 2019-10-17 2019-12-23 2019-10-17 -34.605880 -58.384949 Argentina Capital Federal San Cristobal 7.0 7.0 2.0 140.0 140.0 153000.0 USD ***Venta semipiso centro, ideal hostel***** DESCRIPCION DE LA PROPIEDAD: Departamento de 1... Departamento Venta
1 2019-10-17 2019-11-21 2019-10-17 -34.624056 -58.412110 Argentina Capital Federal Boedo 2.0 1.0 2.0 70.0 58.0 159000.0 USD Espectacular PH reciclado en Boedo sin expensas. PH reciclado en Boedo a una cuadra de la plaz... PH Venta
2 2019-10-17 2019-11-01 2019-10-17 -34.593569 -58.427474 Argentina Capital Federal Palermo 2.0 1.0 1.0 45.0 45.0 125000.0 USD Depto.tipo casa de 2 ambientes en Venta en Pal... 2 ambienets amplio , excelente estado , patio ... PH Venta
3 2019-10-17 2019-12-23 2019-10-17 -34.581294 -58.436754 Argentina Capital Federal Palermo 2.0 1.0 1.0 85.0 50.0 295000.0 USD COSTA RICA 5800 / PALERMO HOLLYWOOD / VENTA PH... HERMOSO PH EN PALERMO!!!2 AMBIENTES TOTALMENTE... PH Venta
4 2019-10-17 2020-03-11 2019-10-17 -34.914194 -57.938219 Argentina Bs.As. G.B.A. Zona Sur La Plata 2.0 1.0 1.0 50.0 35.0 40000.0 USD 58 entre 1 y 2 Venta de departamento en ph.1 ... 58 entre 1 y 2 Venta de departamento en PH. P... PH Venta

df = pd.read_csv(io.BytesIO(data_to_load['DS_Proyecto_01_Datos_Properati.csv'])) df.head()

In [6]:
df.shape
Out[6]:
(146660, 19)
In [7]:
df.columns
Out[7]:
Index(['start_date', 'end_date', 'created_on', 'lat', 'lon', 'l1', 'l2', 'l3',
       'rooms', 'bedrooms', 'bathrooms', 'surface_total', 'surface_covered',
       'price', 'currency', 'title', 'description', 'property_type',
       'operation_type'],
      dtype='object')
In [8]:
features = pd.DataFrame(df.dtypes)
features.columns = ['type']
features_f = features[features['type'] == 'float64'].index
features_o = features[features['type'] == 'object'].index
features.sort_values('type')
Out[8]:
type
bedrooms float64
lat float64
lon float64
price float64
surface_covered float64
rooms float64
surface_total float64
bathrooms float64
description object
title object
currency object
start_date object
l3 object
l2 object
l1 object
created_on object
end_date object
property_type object
operation_type object

Observación: Las variables que debería ser tipo fecha estan siendo leidas como texto

Se verifican los regisros nulos

In [9]:
df.isnull().sum()
Out[9]:
start_date             0
end_date               0
created_on             0
lat                 9925
lon                 9959
l1                     0
l2                     0
l3                     0
rooms                  0
bedrooms               0
bathrooms           5957
surface_total      20527
surface_covered    21614
price                  0
currency               0
title                  0
description            0
property_type          0
operation_type         0
dtype: int64

Se analizan las variables categoricas y numéricas

In [10]:
df.describe(include='object')
Out[10]:
start_date end_date created_on l1 l2 l3 currency title description property_type operation_type
count 146660 146660 146660 146660 146660 146660 146660 146660 146660 146660 146660
unique 366 412 366 1 4 89 1 77733 106668 10 1
top 2019-07-31 9999-12-31 2019-07-31 Argentina Capital Federal Palermo USD DEPARTAMENTO EN VENTA EDIFICIO DE CATEGORIA CON MATERIALES Y ACCESOR... Departamento Venta
freq 11022 25101 11022 146660 92539 13073 146660 5064 235 107326 146660

Observación: Las variables l1, currency y operation_type solo tienen una categoría, por lo que no son útiles en el estudio. Las variables de fecha parecen ser un registro por cada día del año

In [11]:
df['property_type'].value_counts()
Out[11]:
Departamento       107326
Casa                21521
PH                  14298
Lote                 1312
Oficina               658
Otro                  374
Local comercial       325
Casa de campo         322
Depósito              265
Cochera               259
Name: property_type, dtype: int64
In [19]:
unique = df['l3'].unique()

Observación: Solo tres categorías de la variable "Property type" tienen una cantidad importante de registros

In [35]:
text_full_clean =' '.join(df['l3'])
cloud = WordCloud(background_color="green",stopwords = None).generate(text_full_clean.lower())
plt.figure(figsize = (8, 8), facecolor = None) 
plt.imshow(cloud) 
plt.axis("off")
plt.tight_layout(pad = 0)

Observación: El wordCloud nos muestra los barrios con mayor número de propiedades registradas en la plataforma

In [37]:
round(df.describe(include='float64'),2)
Out[37]:
lat lon rooms bedrooms bathrooms surface_total surface_covered price
count 136735.00 136701.00 146660.00 146660.00 140703.00 126133.00 125046.00 146660.00
mean -34.60 -58.47 3.08 1.98 1.60 216.87 112.82 241221.08
std 0.47 0.49 1.42 1.15 0.89 2037.61 916.86 318519.46
min -35.32 -180.00 1.00 0.00 1.00 10.00 1.00 5500.00
25% -34.63 -58.50 2.00 1.00 1.00 52.00 47.00 111000.00
50% -34.60 -58.44 3.00 2.00 1.00 78.00 68.00 166000.00
75% -34.57 -58.41 4.00 3.00 2.00 140.00 108.00 265000.00
max 85.05 -57.81 35.00 15.00 14.00 193549.00 126062.00 32434232.00

Observación: Se evidencia la presencia de outliers y la dimensión de las variables, lo que sugiere la necesidad de algun tratamiento inicial a la data

In [38]:
df['l1'].unique()
Out[38]:
array(['Argentina'], dtype=object)

Observación: Hay varios registros cuya ubicación no corresponde a Argentina

Se analiza gráficamente la distribución de las variables numéricas

In [39]:
plt.rcParams.update({'font.size': 25})
var_hist = features_f
plt.figure(figsize=(25,6*len(features_f)/2))
for i,var in enumerate(var_hist):
    plt.subplot(len(features_f)/2,2,i+1)
    sns.distplot(df[var],fit=stats.norm, kde=False)
    plt.xlabel(var)
    plt.ylabel("count")

Observación: Se evidencia la presencia de outliers y el orden de magnitud de la variables. Lo que evita ver la distribución normal de las mismas

Validación inicial de las variables numéricas y transformación

In [3]:
clean = np.where(
    (df["lat"] < -21) & # norte
    (df["lat"] > -67) & # sur
    (df["lon"] > -73) & # este
    (df["lon"] < -53)  & # oeste
    (df["price"] > 0)  &
    (df["surface_total"] > 0)  &
    (df["surface_covered"] > 0) &
    #(df["surface_total"] > df["surface_covered"])  &
    #(pd.to_numeric(df["end_date"]) < pd.to_numeric(pd.Timestamp.now())) &
    (df["bathrooms"] > 0) #&
    )
df_cleaned = df.iloc[clean].copy()
df_cleaned['start_date_P'] = pd.DatetimeIndex(df_cleaned["start_date"]).month #pd.to_datetime(df_cleaned["start_date"]).dt.to_period("M")
#df_cleaned['end_date_P'] = pd.to_datetime(pd.Timestamp.date(pd.to_datetime(df_cleaned["end_date"]))).dt.to_period("M")
df_cleaned['created_on_P'] = pd.DatetimeIndex(df_cleaned["start_date"]).month #pd.to_datetime(df_cleaned["created_on"]).dt.to_period("M")
#df_cleaned = df_cleaned[df_cleaned.property_type.isin(["Departamento", "Casa", "PH"])]

Observación: Se aplican las observaciones identificadas previamente, para realizar la validación inicial de los datos.

BoxCox Test

In [41]:
features_numeric = ['rooms',	'bathrooms',	'surface_total',	'surface_covered',	'price']
BC_matrix = []
for var in features_numeric:
  BC_matrix.append(stats.boxcox(abs(df_cleaned[var])))
BC_matrix = pd.DataFrame(BC_matrix)
BC_matrix.index = features_numeric
BC_matrix.columns = ['Parametros', 'Lambda']
round(BC_matrix['Lambda'].head(5),2)
Out[41]:
rooms              0.22
bathrooms         -1.69
surface_total     -0.52
surface_covered   -0.31
price             -0.29
Name: Lambda, dtype: float64

Observación: Los valores de lambda negativos para una función BoxCox, recomiendan realizar una transformación logaritmica de las variables numéricas.

In [4]:
df_cleaned['log_Price'] = np.log(df_cleaned['price'])
df_cleaned['log_Price_per_S_total'] = np.log(df_cleaned['price']/df_cleaned["surface_total"])
df_cleaned['log_Price_per_S_covered'] = np.log(df_cleaned['price']/df_cleaned["surface_covered"])
df_cleaned['log_S_total'] = np.log(df_cleaned["surface_total"])
df_cleaned['log_S_covered'] = np.log(df_cleaned["surface_covered"])

Observación: Se realiza la transformación de las variables mas relevantes. Adicionalmente se crean intuitivamente las variables log_Prices_per_S_covered y log_Prices_per_S_total, para analizar los precios por magnitud de área, posteriormente se validara la utilidad de la misma

In [43]:
df_cleaned.shape
Out[43]:
(114488, 26)
In [44]:
'Observación: Después de aplicar la validación, se observa una reducción del {0}% de la cantidad de información'.format(round(len(df_cleaned)/len(df)-1,4)*100)
Out[44]:
'Observación: Después de aplicar la validación, se observa una reducción del -21.94% de la cantidad de información'
In [45]:
features = pd.DataFrame(df_cleaned.dtypes)
features.columns = ['type']
features_f = features[features['type'] == 'float64'].index
features_o = features[features['type'] == 'object'].index
features.shape
#features
Out[45]:
(26, 1)

Se analizan nuevamente las variables numéricas

In [46]:
round(df_cleaned.describe(include='float64'),2)
Out[46]:
lat lon rooms bedrooms bathrooms surface_total surface_covered price log_Price log_Price_per_S_total log_Price_per_S_covered log_S_total log_S_covered
count 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00 114488.00
mean -34.60 -58.46 3.13 2.07 1.57 190.79 111.78 238423.36 12.10 7.57 7.79 4.53 4.31
std 0.10 0.14 1.40 1.09 0.86 1485.21 904.40 294706.97 0.67 0.74 0.53 0.84 0.65
min -35.15 -59.04 1.00 0.00 1.00 10.00 1.00 6000.00 8.70 -0.59 0.29 2.30 0.00
25% -34.62 -58.50 2.00 1.00 1.00 52.00 46.00 115000.00 11.65 7.39 7.55 3.95 3.83
50% -34.60 -58.44 3.00 2.00 1.00 78.00 67.00 168000.00 12.03 7.74 7.87 4.36 4.20
75% -34.57 -58.41 4.00 3.00 2.00 137.00 107.00 264000.00 12.48 7.98 8.10 4.92 4.67
max -34.16 -57.81 35.00 15.00 14.00 169000.00 126062.00 32434232.00 17.29 13.53 13.53 12.04 11.74

Observación: La transformación y validación inicial hacen sentido, pues promedio y la desviación estandar se ajusta con los cuantiles de cada variable nueva

In [47]:
round(df_cleaned.describe(include='int64'))
#df_cleaned.describe(include='object')
Out[47]:
start_date_P created_on_P
count 114488.0 114488.0
mean 6.0 6.0
std 3.0 3.0
min 1.0 1.0
25% 3.0 3.0
50% 6.0 6.0
75% 9.0 9.0
max 12.0 12.0

Observación: Se analiza las variables tipo entero, que son resultado de extraer el mes de las variables de fecha, dado que solo contemplan un periodo de 13 meses, si esta variable comprendiera un periodo mayor si seria interesante ver la variación en un intervalo mayor

Se analiza de nuevo gráficamente la distribución de las variables numéricas, después de la validación y la transforacion de las variables.

In [48]:
var_hist = ['lat',	'lon',	'rooms',	'bedrooms',	'bathrooms',
            'log_Price',	'log_Price_per_S_total',	'log_Price_per_S_covered',	'log_S_total',	'log_S_covered','start_date_P','created_on_P']
len(var_hist)
Out[48]:
12
In [49]:
plt.rcParams.update({'font.size': 20})
plt.figure(figsize=(25,6*5))
for i,var in enumerate(var_hist):
    plt.subplot(6,2,i+1)
    sns.distplot(df_cleaned[var],fit=stats.norm, kde=False, color='g')
    plt.xlabel(var)
    plt.ylabel("count")

Observación: Se puede observar que la mayoría de las variables se distribuyen normal, solo tres variables tienen una disribución con cola derecha, la cual podría ser corregida, de ser necesario.

In [50]:
df_cleaned.isnull().sum()
Out[50]:
start_date                 0
end_date                   0
created_on                 0
lat                        0
lon                        0
l1                         0
l2                         0
l3                         0
rooms                      0
bedrooms                   0
bathrooms                  0
surface_total              0
surface_covered            0
price                      0
currency                   0
title                      0
description                0
property_type              0
operation_type             0
start_date_P               0
created_on_P               0
log_Price                  0
log_Price_per_S_total      0
log_Price_per_S_covered    0
log_S_total                0
log_S_covered              0
dtype: int64

Observación: Ya no se tienen registros con valores nulos o faltantes

Se definen las variables a utilizar finalmente para la continuación del análisis

In [5]:
covariables = ['start_date_P','created_on_P','l2', 'l3', 'property_type','rooms', 'bedrooms', 'bathrooms',
              'lat','lon','log_Price_per_S_total','log_Price_per_S_covered','log_S_total','log_S_covered','price','log_Price']
len(covariables)
Out[5]:
16

Distribución de las variables o predictores respecto a la variable dependiente

In [52]:
plt.rcParams.update({'font.size': 20})
hight = 10
wide = 20
col = 2
row = 8
plt.figure(figsize=(wide*col,hight*row))
for i,var in enumerate(covariables):
    plt.subplot(row,col,i+1)
    if var in ['start_date_P','created_on_P','l2', 'l3','bathrooms','rooms','bedrooms','property_type']:
        sns.violinplot(x=var, y='log_Price', data=df_cleaned)
        plt.xticks(rotation=90)
    else:
        sns.scatterplot(x=var,y='log_Price',data=df_cleaned, alpha=0.10,color='r')

Observación: A través de las gráficas de violines, se puede ver la distribuición de las variables categoricas con respecto a la variable dependiente, si bien no se percibe alguna tendencia en ellas, en particular la variable rooms si muestra los outliers que tiene. Por su parte los gráficos de dispersión permiten ver la distribución de las variables numéricas con respecto a la variable independiente, sin embargo no muestra un claro indicio de heterocedasticidad o tendencia. Las últimas dos gráficas permiten visualizar la transformación realizada a la variable dependiente.

Análisis de correlaciones

In [54]:
plt.rcParams.update({'font.size': 10})
#compute correlation matrix
df_correlations = df_cleaned.corr()

#mask the upper half for visualization purposes
mask = np.zeros_like(df_correlations, dtype=np.bool)
mask[np.triu_indices_from(mask)] = True

# Draw the heatmap with the mask and correct aspect ratio
plt.figure(figsize= (10,10))
sns.heatmap(df_correlations, mask=mask, cmap="GnBu_r",#"RdYlBu", 
    annot=True, square=True,
    #vmin=-0.9, vmax=0.9,
    fmt="+.1f")
plt.title("Correlations between predictors")
Out[54]:
Text(0.5, 1.0, 'Correlations between predictors')

Observación: El analisis de correlaciones nos permite identifficar que variables numericas pueden representar una posible multicolinealidad en los modelos a realizar.

Las correlaciones mas fuertes se evidencian entre:

  1. Lat vs Lon
  2. Bedrooms vs Rooms
  3. start_date_p vs Created_on_p
  4. Log_Price vs batrooms
  5. Log_Price vs price
  6. Log_Price_per_S_covered vs Log_Price_per_S_total
  7. Log_S_total vs rooms
  8. Log_S_total vs bedrooms
  9. Log_S_total vs bathrooms
  10. Log_S_total vs Log_Price_per_S_total
  11. Log_S_covered vs rooms
  12. Log_S_covered vs bedrooms
  13. Log_S_covered vs bathrooms
  14. Log_S_covered vs Log_S_Price
  15. Log_S_covered vs Log_S_total
In [55]:
covariables
Out[55]:
['start_date_P',
 'created_on_P',
 'l2',
 'l3',
 'property_type',
 'rooms',
 'bedrooms',
 'bathrooms',
 'lat',
 'lon',
 'log_Price_per_S_total',
 'log_Price_per_S_covered',
 'log_S_total',
 'log_S_covered',
 'price',
 'log_Price']
In [70]:
covariables = ['start_date_P',
          #'created_on_P',
          'l2',
          'l3',
          'property_type',
          'rooms',
          'bedrooms',
          'bathrooms',
          'lat',
          'lon',
          'log_Price_per_S_total',
          #'log_Price_per_S_covered',
          #'log_S_total',
          'log_S_covered'
          #'price',
          #'log_Price'
          ]
print(len(covariables))
formula = ' + '.join(covariables)
'log_Price~'+formula
11
Out[70]:
'log_Price~start_date_P + l2 + l3 + property_type + rooms + bedrooms + bathrooms + lat + lon + log_Price_per_S_total + log_S_covered'

Observación: Se descartan las covariables que presentan una fuerte correlación fruto de relaciones inmediatas que presentan con otras variables.

In [71]:
plt.rcParams.update({'font.size': 15})
df_temp = df_cleaned[covariables]
#compute correlation matrix
df_correlations = df_temp.corr()

#mask the upper half for visualization purposes
mask = np.zeros_like(df_correlations, dtype=np.bool)
mask[np.triu_indices_from(mask)] = True

# Draw the heatmap with the mask and correct aspect ratio
plt.figure(figsize= (10,10))
sns.heatmap(df_correlations, mask=mask, cmap="GnBu_r",#"RdYlBu", 
    annot=True, square=True,
    #vmin=-0.9, vmax=0.9,
    fmt="+.1f")
plt.title("Correlations between predictors")
Out[71]:
Text(0.5, 1.0, 'Correlations between predictors')

Observación: Aun existen correlaciones altas entre Lon vs Lat y Bedrooms vs roooms, se verificara su p-value en el analisis estadistico a realizar

Primer Modelo - Regresión lineal

Partición de la información

In [7]:
np.random.seed(1)
ndata = len(df_cleaned)
# Randomly choose 0.8n indices between 1 and n
idx_train = np.random.choice(range(ndata),int(0.7*ndata),replace=False)
# The test set is comprised from all the indices that were
# not selected in the training set:
idx_test  = np.asarray(list(set(range(ndata)) - set(idx_train)))
train     = df_cleaned.iloc[idx_train] # the training data set
test      = df_cleaned.iloc[idx_test]  # the test data set
print(train.shape)
print(test.shape)
(80141, 26)
(34347, 26)

Observación Se realiza la partición de la información 70/30 para probar el modelo creando asi los conjuntos de prueba y entrenamiento

In [59]:
df_cleaned.columns
Out[59]:
Index(['start_date', 'end_date', 'created_on', 'lat', 'lon', 'l1', 'l2', 'l3',
       'rooms', 'bedrooms', 'bathrooms', 'surface_total', 'surface_covered',
       'price', 'currency', 'title', 'description', 'property_type',
       'operation_type', 'start_date_P', 'created_on_P', 'log_Price',
       'log_Price_per_S_total', 'log_Price_per_S_covered', 'log_S_total',
       'log_S_covered'],
      dtype='object')

Entrenamiento del Modelo

In [58]:
model_rl= smf.ols(formula = "log_Price~start_date_P + l2 + l3 + property_type + rooms + bedrooms + bathrooms + lat + lon + log_Price_per_S_total + log_S_covered", data = train).fit()
print(model_rl.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:              log_Price   R-squared:                       0.854
Model:                            OLS   Adj. R-squared:                  0.854
Method:                 Least Squares   F-statistic:                     4450.
Date:                Fri, 04 Sep 2020   Prob (F-statistic):               0.00
Time:                        17:14:11   Log-Likelihood:                -4941.7
No. Observations:               80141   AIC:                         1.010e+04
Df Residuals:                   80035   BIC:                         1.108e+04
Df Model:                         105                                         
Covariance Type:            nonrobust                                         
====================================================================================================
                                       coef    std err          t      P>|t|      [0.025      0.975]
----------------------------------------------------------------------------------------------------
Intercept                           42.1073      3.438     12.246      0.000      35.368      48.846
l2[T.Bs.As. G.B.A. Zona Oeste]       3.0481      0.249     12.230      0.000       2.560       3.537
l2[T.Bs.As. G.B.A. Zona Sur]         3.5265      0.255     13.843      0.000       3.027       4.026
l2[T.Capital Federal]                3.4750      0.265     13.101      0.000       2.955       3.995
l3[T.Agronomía]                      0.0445      0.029      1.560      0.119      -0.011       0.100
l3[T.Almagro]                        0.0311      0.016      1.953      0.051      -0.000       0.062
l3[T.Almirante Brown]                0.1268      0.026      4.909      0.000       0.076       0.177
l3[T.Avellaneda]                    -0.1551      0.016     -9.553      0.000      -0.187      -0.123
l3[T.Balvanera]                     -0.0828      0.017     -4.932      0.000      -0.116      -0.050
l3[T.Barracas]                       0.0916      0.020      4.670      0.000       0.053       0.130
l3[T.Barrio Norte]                   0.1371      0.017      8.247      0.000       0.105       0.170
l3[T.Belgrano]                       0.1587      0.016      9.721      0.000       0.127       0.191
l3[T.Berazategui]                    0.2768      0.019     14.793      0.000       0.240       0.313
l3[T.Boca]                          -0.1228      0.024     -5.047      0.000      -0.170      -0.075
l3[T.Boedo]                          0.0027      0.019      0.140      0.888      -0.035       0.041
l3[T.Caballito]                      0.0680      0.016      4.222      0.000       0.036       0.100
l3[T.Catalinas]                     -0.0478      0.150     -0.320      0.749      -0.341       0.245
l3[T.Cañuelas]                       0.8137      0.054     15.186      0.000       0.709       0.919
l3[T.Centro / Microcentro]          -0.0827      0.021     -3.915      0.000      -0.124      -0.041
l3[T.Chacarita]                      0.0245      0.020      1.238      0.216      -0.014       0.063
l3[T.Coghlan]                        0.0786      0.021      3.730      0.000       0.037       0.120
l3[T.Colegiales]                     0.1120      0.018      6.274      0.000       0.077       0.147
l3[T.Congreso]                      -0.0734      0.020     -3.688      0.000      -0.112      -0.034
l3[T.Constitución]                  -0.1929      0.022     -8.659      0.000      -0.237      -0.149
l3[T.Escobar]                        3.0760      0.266     11.564      0.000       2.555       3.597
l3[T.Esteban Echeverría]             0.2091      0.028      7.599      0.000       0.155       0.263
l3[T.Ezeiza]                         0.3503      0.031     11.212      0.000       0.289       0.412
l3[T.Florencio Varela]               0.1940      0.034      5.697      0.000       0.127       0.261
l3[T.Flores]                        -0.0232      0.017     -1.350      0.177      -0.057       0.010
l3[T.Floresta]                      -0.0440      0.019     -2.270      0.023      -0.082      -0.006
l3[T.General Rodríguez]              0.5201      0.040     13.000      0.000       0.442       0.598
l3[T.General San Martín]             3.1900      0.267     11.929      0.000       2.666       3.714
l3[T.Hurlingham]                     0.1490      0.029      5.089      0.000       0.092       0.206
l3[T.Ituzaingó]                      0.3421      0.030     11.269      0.000       0.283       0.402
l3[T.José C Paz]                     3.0046      0.273     11.015      0.000       2.470       3.539
l3[T.La Matanza]                     0.3303      0.026     12.682      0.000       0.279       0.381
l3[T.La Plata]                       0.3752      0.020     18.890      0.000       0.336       0.414
l3[T.Lanús]                         -0.1477      0.018     -8.138      0.000      -0.183      -0.112
l3[T.Las Cañitas]                    0.2392      0.020     11.943      0.000       0.200       0.278
l3[T.Liniers]                       -0.0144      0.021     -0.699      0.485      -0.055       0.026
l3[T.Lomas de Zamora]                0.0083      0.021      0.401      0.688      -0.032       0.049
l3[T.Malvinas Argentinas]            3.0846      0.270     11.434      0.000       2.556       3.613
l3[T.Marcos Paz]                     0.8122      0.078     10.470      0.000       0.660       0.964
l3[T.Mataderos]                      0.0078      0.021      0.367      0.713      -0.034       0.049
l3[T.Merlo]                          0.1791      0.034      5.200      0.000       0.112       0.247
l3[T.Monserrat]                     -0.0869      0.019     -4.501      0.000      -0.125      -0.049
l3[T.Monte Castro]                  -0.0003      0.021     -0.014      0.989      -0.041       0.041
l3[T.Moreno]                         0.2588      0.033      7.903      0.000       0.195       0.323
l3[T.Morón]                          0.2665      0.027      9.965      0.000       0.214       0.319
l3[T.Nuñez]                          0.1161      0.018      6.597      0.000       0.082       0.151
l3[T.Once]                          -0.0736      0.019     -3.815      0.000      -0.111      -0.036
l3[T.Palermo]                        0.2045      0.016     12.926      0.000       0.173       0.235
l3[T.Parque Avellaneda]             -0.0409      0.026     -1.549      0.121      -0.093       0.011
l3[T.Parque Centenario]              0.0192      0.018      1.081      0.280      -0.016       0.054
l3[T.Parque Chacabuco]              -0.0342      0.019     -1.765      0.078      -0.072       0.004
l3[T.Parque Chas]                    0.0088      0.025      0.346      0.729      -0.041       0.058
l3[T.Parque Patricios]              -0.0389      0.022     -1.806      0.071      -0.081       0.003
l3[T.Paternal]                      -0.0543      0.019     -2.917      0.004      -0.091      -0.018
l3[T.Pilar]                          3.1406      0.273     11.511      0.000       2.606       3.675
l3[T.Pompeya]                       -0.1105      0.027     -4.086      0.000      -0.164      -0.057
l3[T.Presidente Perón]               0.8554      0.043     20.027      0.000       0.772       0.939
l3[T.Puerto Madero]                  0.6016      0.018     33.629      0.000       0.567       0.637
l3[T.Quilmes]                        0.0061      0.014      0.418      0.676      -0.022       0.034
l3[T.Recoleta]                       0.2044      0.016     12.654      0.000       0.173       0.236
l3[T.Retiro]                         0.1033      0.019      5.363      0.000       0.066       0.141
l3[T.Saavedra]                       0.0258      0.019      1.360      0.174      -0.011       0.063
l3[T.San Cristobal]                 -0.0627      0.018     -3.387      0.001      -0.099      -0.026
l3[T.San Fernando]                   3.2184      0.263     12.252      0.000       2.704       3.733
l3[T.San Isidro]                     3.4066      0.263     12.954      0.000       2.891       3.922
l3[T.San Miguel]                     3.1311      0.272     11.502      0.000       2.598       3.665
l3[T.San Nicolás]                   -0.0874      0.020     -4.476      0.000      -0.126      -0.049
l3[T.San Telmo]                      0.0391      0.019      2.093      0.036       0.002       0.076
l3[T.San Vicente]                    0.6136      0.039     15.828      0.000       0.538       0.690
l3[T.Tigre]                          3.2932      0.263     12.506      0.000       2.777       3.809
l3[T.Tres de Febrero]                0.1900      0.023      8.195      0.000       0.145       0.235
l3[T.Tribunales]                    -0.1401      0.029     -4.910      0.000      -0.196      -0.084
l3[T.Velez Sarsfield]               -0.0108      0.032     -0.341      0.733      -0.073       0.051
l3[T.Versalles]                      0.0184      0.027      0.682      0.495      -0.035       0.071
l3[T.Vicente López]                  3.5126      0.264     13.327      0.000       2.996       4.029
l3[T.Villa Crespo]                   0.0094      0.016      0.583      0.560      -0.022       0.041
l3[T.Villa Devoto]                   0.0968      0.018      5.306      0.000       0.061       0.133
l3[T.Villa General Mitre]           -0.0469      0.023     -2.031      0.042      -0.092      -0.002
l3[T.Villa Lugano]                  -0.1930      0.022     -8.804      0.000      -0.236      -0.150
l3[T.Villa Luro]                     0.0444      0.022      2.059      0.040       0.002       0.087
l3[T.Villa Ortuzar]                  0.0347      0.024      1.419      0.156      -0.013       0.083
l3[T.Villa Pueyrredón]               0.0541      0.020      2.699      0.007       0.015       0.093
l3[T.Villa Real]                    -0.0201      0.031     -0.652      0.515      -0.080       0.040
l3[T.Villa Riachuelo]                0.0726      0.060      1.211      0.226      -0.045       0.190
l3[T.Villa Santa Rita]               0.0116      0.024      0.483      0.629      -0.035       0.059
l3[T.Villa Soldati]                 -0.4004      0.045     -8.940      0.000      -0.488      -0.313
l3[T.Villa Urquiza]                  0.0892      0.017      5.286      0.000       0.056       0.122
l3[T.Villa del Parque]               0.0492      0.018      2.722      0.006       0.014       0.085
property_type[T.Casa de campo]       0.8011      0.021     37.770      0.000       0.760       0.843
property_type[T.Cochera]            -1.0179      0.258     -3.951      0.000      -1.523      -0.513
property_type[T.Departamento]       -0.1391      0.004    -33.061      0.000      -0.147      -0.131
property_type[T.Depósito]           -0.3537      0.129     -2.743      0.006      -0.606      -0.101
property_type[T.Local comercial]    -0.3480      0.091     -3.813      0.000      -0.527      -0.169
property_type[T.Lote]                0.2404      0.059      4.060      0.000       0.124       0.356
property_type[T.Oficina]            -0.1624      0.018     -9.261      0.000      -0.197      -0.128
property_type[T.Otro]               -0.0279      0.033     -0.849      0.396      -0.092       0.037
property_type[T.PH]                 -0.1486      0.004    -34.249      0.000      -0.157      -0.140
start_date_P                        -0.0004      0.000     -1.323      0.186      -0.001       0.000
rooms                                0.0436      0.002     24.974      0.000       0.040       0.047
bedrooms                             0.0021      0.002      0.976      0.329      -0.002       0.006
bathrooms                            0.1081      0.002     69.217      0.000       0.105       0.111
lat                                  1.4997      0.066     22.808      0.000       1.371       1.629
lon                                 -0.2036      0.044     -4.579      0.000      -0.291      -0.116
log_Price_per_S_total                0.4468      0.002    205.852      0.000       0.443       0.451
log_S_covered                        0.6735      0.003    251.623      0.000       0.668       0.679
==============================================================================
Omnibus:                    21715.926   Durbin-Watson:                   1.997
Prob(Omnibus):                  0.000   Jarque-Bera (JB):          2225466.752
Skew:                           0.133   Prob(JB):                         0.00
Kurtosis:                      28.815   Cond. No.                     5.83e+15
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The smallest eigenvalue is 1.12e-23. This might indicate that there are
strong multicollinearity problems or that the design matrix is singular.
In [59]:
resultados1 = ('modelo: rl R²: {}, AIC: {}, RMSE: {}, MAE: {} y MAPE: {}%'.format(
     round(model_rl.rsquared,2),
     round(model_rl.aic,2),
     round(RMSE(np.exp(model_rl.predict(test)),test.price),2),
     round(MAE(np.exp(model_rl.predict(test)),test.price),2),
     round(MAPE(np.exp(model_rl.predict(test)),test.price),2))
   )
print(resultados1)
modelo: rl R²: 0.85, AIC: 10095.46, RMSE: 249830.77, MAE: 49042.61 y MAPE: 19.05%

Observación: El modelo inicial de regresión lineal contempla las variables numericas y categoricas definidas en el paso anterior. Sin embargo, a pesar de que el modelo presenta un R² alto del 85% y un MAPE del 19.05%, vemos que el AIC y el número condicional tambien son considerablemente altos lo que implica la presencia de multicolinealidad en las covariables.

In [77]:
p = model_rl.pvalues
print(p[p<=0.05/11].tail(15))
print(p[p<=0.05/11].head(5))
l3[T.Villa Soldati]                  3.984979e-19
l3[T.Villa Urquiza]                  1.256454e-07
property_type[T.Casa de campo]      1.899656e-309
property_type[T.Cochera]             7.802195e-05
property_type[T.Departamento]       4.375524e-238
property_type[T.Local comercial]     1.375096e-04
property_type[T.Lote]                4.916337e-05
property_type[T.Oficina]             2.065985e-20
property_type[T.PH]                 3.176974e-255
rooms                               3.922612e-137
bathrooms                            0.000000e+00
lat                                 8.987317e-115
lon                                  4.669586e-06
log_Price_per_S_total                0.000000e+00
log_S_covered                        0.000000e+00
dtype: float64
Intercept                         1.886265e-34
l2[T.Bs.As. G.B.A. Zona Oeste]    2.307331e-34
l2[T.Bs.As. G.B.A. Zona Sur]      1.573994e-43
l2[T.Capital Federal]             3.565094e-39
l3[T.Almirante Brown]             9.151707e-07
dtype: float64

Observación: Los p-Value mayores a 5% se presentan en diferentes barrios, asi mismo la variable _propertyType presenta un p-value alto, por lo menos para la categoría Depósito; indicando insuficiencia estadistica en estas variables, lo que sugiere descartarla para reducir su multicolienalidad, sin emnbargo esto no ocurre para tadas las categorias, por lo cual se matienen esta variables dentro del estudio.

In [65]:
df2 = df_cleaned.copy()
df2['l3'].value_counts()
Out[65]:
Palermo             11211
Almagro              6655
Tigre                6069
Villa Crespo         5835
Caballito            5562
                    ...  
Presidente Perón       67
Villa Soldati          65
Villa Riachuelo        33
Marcos Paz             21
Catalinas               5
Name: l3, Length: 89, dtype: int64
In [66]:
df2 = df_cleaned.copy()
df2['property_type'].value_counts()
Out[66]:
Departamento       85071
Casa               15162
PH                 13530
Oficina              336
Casa de campo        240
Otro                  98
Lote                  29
Local comercial       15
Depósito               5
Cochera                2
Name: property_type, dtype: int64

Observación: La variable de barrios precisamente indica que tiene categorías con muy pocos registros, por su parte, la covariable _propertyType solo cuenta con tres categorías con un cantidad significativa de registros, lo que muestra un claro desbalance en la información.

Es por esto que se puede realizar un segundo modelo que se enfoque en una categoría que tenga datos suficientes para no presentar un desbalance significativo.

Segundo Modelo - Regresión lineal - Departamentos

In [60]:
property_type = ['Departamento']#, 'PH']#, 'Casa', 'Casa de campo', 'Oficina']
df2 = df_cleaned.copy()
for i in property_type:
  df2 = df2[df2['property_type'] == 'Departamento']
  #Particion
  np.random.seed(1)
  ndata = len(df2)
  # Randomly choose 0.8n indices between 1 and n
  idx_train = np.random.choice(range(ndata),int(0.8*ndata),replace=False)
  # The test set is comprised from all the indices that were
  # not selected in the training set:
  idx_test = np.asarray(list(set(range(ndata)) - set(idx_train)))
  train2 = df2.iloc[idx_train] # the training data set
  test2 = df2.iloc[idx_test]  # the test data set
  #RunModel
  model_rl2 = smf.ols(formula = "log_Price~start_date_P + l2 + l3 + property_type + rooms + bedrooms + bathrooms + lat + lon + log_Price_per_S_total+ log_S_covered", data = train2).fit()
  #Parametros
  print(model_rl2.summary())
  resultados2 = ('modelo: rl2 {}, R²: {}, AIC: {}, RMSE: {}, MAE: {} y MAPE: {}%'.format(i,
         round(model_rl2.rsquared,2),
         round(model_rl2.aic,2),
         round(RMSE(np.exp(model_rl2.predict(test2)),test2.price),2),
         round(MAE(np.exp(model_rl2.predict(test2)),test2.price),2),
         round(MAPE(np.exp(model_rl2.predict(test2)),test2.price),2))
       )
  print(resultados2)
                            OLS Regression Results                            
==============================================================================
Dep. Variable:              log_Price   R-squared:                       0.915
Model:                            OLS   Adj. R-squared:                  0.915
Method:                 Least Squares   F-statistic:                     7623.
Date:                Fri, 04 Sep 2020   Prob (F-statistic):               0.00
Time:                        17:15:10   Log-Likelihood:                 13424.
No. Observations:               68056   AIC:                        -2.665e+04
Df Residuals:                   67959   BIC:                        -2.577e+04
Df Model:                          96                                         
Covariance Type:            nonrobust                                         
==================================================================================================
                                     coef    std err          t      P>|t|      [0.025      0.975]
--------------------------------------------------------------------------------------------------
Intercept                         61.3796      3.986     15.398      0.000      53.567      69.193
l2[T.Bs.As. G.B.A. Zona Oeste]     4.4036      0.290     15.180      0.000       3.835       4.972
l2[T.Bs.As. G.B.A. Zona Sur]       4.8510      0.300     16.193      0.000       4.264       5.438
l2[T.Capital Federal]              4.9354      0.309     15.994      0.000       4.331       5.540
l3[T.Agronomía]                    0.0467      0.027      1.731      0.083      -0.006       0.099
l3[T.Almagro]                      0.0246      0.012      2.014      0.044       0.001       0.049
l3[T.Almirante Brown]              0.2898      0.040      7.260      0.000       0.212       0.368
l3[T.Avellaneda]                   0.0972      0.023      4.275      0.000       0.053       0.142
l3[T.Balvanera]                   -0.0672      0.013     -5.197      0.000      -0.092      -0.042
l3[T.Barracas]                     0.1364      0.016      8.545      0.000       0.105       0.168
l3[T.Barrio Norte]                 0.0672      0.013      5.298      0.000       0.042       0.092
l3[T.Belgrano]                     0.0780      0.013      6.115      0.000       0.053       0.103
l3[T.Berazategui]                  0.3466      0.027     12.897      0.000       0.294       0.399
l3[T.Boca]                        -0.0560      0.019     -2.895      0.004      -0.094      -0.018
l3[T.Boedo]                        0.0155      0.016      0.984      0.325      -0.015       0.046
l3[T.Caballito]                    0.0573      0.012      4.608      0.000       0.033       0.082
l3[T.Catalinas]                   -0.1044      0.141     -0.740      0.460      -0.381       0.172
l3[T.Cañuelas]                     0.7022      0.088      7.994      0.000       0.530       0.874
l3[T.Centro / Microcentro]        -0.0757      0.016     -4.762      0.000      -0.107      -0.045
l3[T.Chacarita]                    0.0084      0.016      0.529      0.597      -0.023       0.040
l3[T.Coghlan]                      0.0511      0.017      3.082      0.002       0.019       0.084
l3[T.Colegiales]                   0.0741      0.014      5.342      0.000       0.047       0.101
l3[T.Congreso]                    -0.0669      0.015     -4.339      0.000      -0.097      -0.037
l3[T.Constitución]                -0.1122      0.017     -6.435      0.000      -0.146      -0.078
l3[T.Escobar]                      4.5572      0.307     14.836      0.000       3.955       5.159
l3[T.Esteban Echeverría]           0.3585      0.039      9.139      0.000       0.282       0.435
l3[T.Ezeiza]                       0.5633      0.040     14.227      0.000       0.486       0.641
l3[T.Florencio Varela]             0.3696      0.049      7.602      0.000       0.274       0.465
l3[T.Flores]                       0.0190      0.014      1.399      0.162      -0.008       0.046
l3[T.Floresta]                    -0.0140      0.016     -0.880      0.379      -0.045       0.017
l3[T.General Rodríguez]            0.5379      0.056      9.523      0.000       0.427       0.649
l3[T.General San Martín]           4.7970      0.311     15.443      0.000       4.188       5.406
l3[T.Hurlingham]                   0.3324      0.043      7.673      0.000       0.248       0.417
l3[T.Ituzaingó]                    0.5640      0.042     13.458      0.000       0.482       0.646
l3[T.José C Paz]                   4.5712      0.317     14.440      0.000       3.951       5.192
l3[T.La Matanza]                   0.5493      0.032     16.933      0.000       0.486       0.613
l3[T.La Plata]                     0.4803      0.029     16.756      0.000       0.424       0.537
l3[T.Lanús]                        0.1482      0.025      5.887      0.000       0.099       0.198
l3[T.Las Cañitas]                  0.1166      0.015      7.641      0.000       0.087       0.147
l3[T.Liniers]                      0.0587      0.017      3.379      0.001       0.025       0.093
l3[T.Lomas de Zamora]              0.2867      0.028     10.167      0.000       0.231       0.342
l3[T.Malvinas Argentinas]          4.5300      0.314     14.435      0.000       3.915       5.145
l3[T.Marcos Paz]                   0.6245      0.135      4.642      0.000       0.361       0.888
l3[T.Mataderos]                    0.0998      0.019      5.131      0.000       0.062       0.138
l3[T.Merlo]                        0.4105      0.044      9.230      0.000       0.323       0.498
l3[T.Monserrat]                   -0.0570      0.015     -3.867      0.000      -0.086      -0.028
l3[T.Monte Castro]                 0.0256      0.017      1.485      0.138      -0.008       0.059
l3[T.Moreno]                       0.4740      0.041     11.576      0.000       0.394       0.554
l3[T.Morón]                        0.4950      0.033     14.906      0.000       0.430       0.560
l3[T.Nuñez]                        0.0578      0.014      4.149      0.000       0.030       0.085
l3[T.Once]                        -0.0509      0.015     -3.441      0.001      -0.080      -0.022
l3[T.Palermo]                      0.1217      0.012      9.985      0.000       0.098       0.146
l3[T.Parque Avellaneda]            0.0241      0.029      0.828      0.408      -0.033       0.081
l3[T.Parque Centenario]            0.0067      0.014      0.495      0.621      -0.020       0.033
l3[T.Parque Chacabuco]             0.0374      0.016      2.332      0.020       0.006       0.069
l3[T.Parque Chas]               3.397e-05      0.022      0.002      0.999      -0.044       0.044
l3[T.Parque Patricios]            -0.0203      0.018     -1.109      0.268      -0.056       0.016
l3[T.Paternal]                    -0.0065      0.015     -0.428      0.668      -0.036       0.023
l3[T.Pilar]                        4.7241      0.314     15.039      0.000       4.108       5.340
l3[T.Pompeya]                     -0.0940      0.030     -3.182      0.001      -0.152      -0.036
l3[T.Presidente Perón]             0.4149      0.188      2.212      0.027       0.047       0.783
l3[T.Puerto Madero]                0.3583      0.014     26.089      0.000       0.331       0.385
l3[T.Quilmes]                      0.2151      0.022      9.786      0.000       0.172       0.258
l3[T.Recoleta]                     0.1039      0.012      8.386      0.000       0.080       0.128
l3[T.Retiro]                       0.0238      0.015      1.641      0.101      -0.005       0.052
l3[T.Saavedra]                     0.0204      0.015      1.325      0.185      -0.010       0.051
l3[T.San Cristobal]               -0.0222      0.014     -1.534      0.125      -0.051       0.006
l3[T.San Fernando]                 4.7410      0.304     15.593      0.000       4.145       5.337
l3[T.San Isidro]                   4.8763      0.304     16.018      0.000       4.280       5.473
l3[T.San Miguel]                   4.6885      0.315     14.883      0.000       4.071       5.306
l3[T.San Nicolás]                 -0.0721      0.015     -4.808      0.000      -0.101      -0.043
l3[T.San Telmo]                    0.0568      0.014      3.929      0.000       0.028       0.085
l3[T.San Vicente]                  0.5784      0.080      7.197      0.000       0.421       0.736
l3[T.Tigre]                        4.7474      0.304     15.620      0.000       4.152       5.343
l3[T.Tres de Febrero]              0.4161      0.029     14.317      0.000       0.359       0.473
l3[T.Tribunales]                  -0.1170      0.022     -5.400      0.000      -0.159      -0.075
l3[T.Velez Sarsfield]              0.0270      0.029      0.930      0.353      -0.030       0.084
l3[T.Versalles]                    0.0177      0.026      0.686      0.493      -0.033       0.068
l3[T.Vicente López]                4.9568      0.306     16.220      0.000       4.358       5.556
l3[T.Villa Crespo]                 0.0086      0.012      0.694      0.488      -0.016       0.033
l3[T.Villa Devoto]                 0.1075      0.015      7.080      0.000       0.078       0.137
l3[T.Villa General Mitre]         -0.0038      0.020     -0.193      0.847      -0.043       0.035
l3[T.Villa Lugano]                -0.1469      0.019     -7.558      0.000      -0.185      -0.109
l3[T.Villa Luro]                   0.1059      0.018      5.839      0.000       0.070       0.141
l3[T.Villa Ortuzar]               -0.0135      0.022     -0.616      0.538      -0.056       0.029
l3[T.Villa Pueyrredón]             0.0438      0.017      2.618      0.009       0.011       0.077
l3[T.Villa Real]                  -0.0241      0.028     -0.853      0.393      -0.079       0.031
l3[T.Villa Riachuelo]              0.1554      0.076      2.032      0.042       0.005       0.305
l3[T.Villa Santa Rita]             0.0208      0.020      1.039      0.299      -0.018       0.060
l3[T.Villa Soldati]               -0.2416      0.042     -5.763      0.000      -0.324      -0.159
l3[T.Villa Urquiza]                0.0725      0.013      5.469      0.000       0.046       0.098
l3[T.Villa del Parque]             0.0527      0.015      3.608      0.000       0.024       0.081
start_date_P                      -0.0006      0.000     -2.549      0.011      -0.001      -0.000
rooms                              0.0550      0.002     30.119      0.000       0.051       0.059
bedrooms                          -0.0043      0.002     -1.980      0.048      -0.009   -4.27e-05
bathrooms                          0.0954      0.001     66.358      0.000       0.093       0.098
lat                                1.6979      0.081     20.979      0.000       1.539       1.857
lon                                0.0695      0.047      1.488      0.137      -0.022       0.161
log_Price_per_S_total              0.6586      0.002    278.509      0.000       0.654       0.663
log_S_covered                      0.7459      0.003    288.581      0.000       0.741       0.751
==============================================================================
Omnibus:                    36765.446   Durbin-Watson:                   2.000
Prob(Omnibus):                  0.000   Jarque-Bera (JB):         22899469.194
Skew:                           1.228   Prob(JB):                         0.00
Kurtosis:                      92.830   Cond. No.                     1.00e+16
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The smallest eigenvalue is 3.22e-24. This might indicate that there are
strong multicollinearity problems or that the design matrix is singular.
modelo: rl2 Departamento, R²: 0.92, AIC: -26653.05, RMSE: 222178.15, MAE: 33221.75 y MAPE: 12.79%

Observación: El segundo modelo de regresión lineal se realiza con las mismas variables que el primero,con excepción de que se realizó específicamente para el tipo de propiedad "Departamento", de acuerdo con lo indentificado en el paso anterior. Sin embargo, a pesar de que el modelo presenta un R² alto del 92% y un AIC menor que cero, vemos que el MAPE se redujo 7 puntos porcetuales.

In [61]:
print(resultados1)
print(resultados2)
modelo: rl R²: 0.85, AIC: 10095.46, RMSE: 249830.77, MAE: 49042.61 y MAPE: 19.05%
modelo: rl2 Departamento, R²: 0.92, AIC: -26653.05, RMSE: 222178.15, MAE: 33221.75 y MAPE: 12.79%

Observación: Con el segundo modelo de regresión lineal podemos realizar las predicciones del precio de un nuevo departamento, mientras que con el primero podemos realizar la predicción del precio de cualquier tipo de propiedad, solo que con un mayor margen de error, por ello es pertinente analizar los residuales de ambos modelos.

Residuales

In [62]:
#plt.figure(figsize=(7,5))
sns.distplot(model_rl.resid,fit=stats.norm, kde=False, label='modelo_inicial')
sns.distplot(model_rl2.resid,fit=stats.norm, kde=False, label='modelo_Departamentos')
plt.legend()
plt.ylabel("count")
plt.title('Residuals')
Out[62]:
Text(0.5, 1.0, 'Residuals')

Observación: Los residuales del "modelo_departamentos" presentan menor desviación estandar y mayor concentración en la media.

3. Desafio

¿Qué caracteriza a las propiedades mas costosas?

In [69]:
plt.figure(figsize=(15,10))
sns.scatterplot(x='lon', y='lat', data=df_cleaned, hue="l2",
               size="price")
plt.title("Ciudad de Buenos Aires",fontsize=20)
Out[69]:
Text(0.5, 1.0, 'Ciudad de Buenos Aires')

Observación: Visualizamos geográficamente como esta distribuida la información del estudio, por cada una de las zonas de Buenos Aires.

In [70]:
df_cleaned['Area_price'] = 'Medio'
min_index = df_cleaned[df_cleaned['log_Price_per_S_total'] < np.percentile(df_cleaned['log_Price_per_S_total'], 25)].index
max_index = df_cleaned[df_cleaned['log_Price_per_S_total'] > np.percentile(df_cleaned['log_Price_per_S_total'], 75)].index
df_cleaned.loc[min_index,'Area_price'] ='Bajo'
df_cleaned.loc[max_index,'Area_price'] ='Alto'
df_cleaned['Area_price'].unique()
Out[70]:
array(['Bajo', 'Medio', 'Alto'], dtype=object)

Observación: Se crea la variable _Areprice para identificar las priedades de mayor valor, aquellas superiores al 75% del precio por área de las propiedades regisradas en la plataforma

In [71]:
plt.figure(figsize=(15,10))
sns.scatterplot(x='lon', y='lat', data=df_cleaned, hue="Area_price",
               size="surface_total")
plt.title("Valor de las propiedades",fontsize=20)
Out[71]:
Text(0.5, 1.0, 'Valor de las propiedades')

Observación: Se puede observa cómo se distribuye geográficamente el valor de las diferentes propiedades, en el centro de la ciudad se aprecian las mas costosas, mientras que en la zona periferica se observan las propiedades de menor valuación.

Propiedades de mayor costo por Área

In [92]:
df_CA = df_cleaned[df_cleaned['Area_price']=='Alto']
max_amount = float(df_CA['price'].max())

folium_hmap = folium.Map(location=[-34.6, -58.4],
                        zoom_start=13,
                        tiles="OpenStreetMap")

hm_wide = HeatMap( list(zip(df_CA['lat'], df_CA['lon'], df_CA['price'])),
                   min_opacity=0.2,
                   max_val=max_amount,
                   radius=8, blur=6, 
                   max_zoom=15, 
                 )

folium_hmap.add_child(hm_wide)
Out[92]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [90]:
df_CA['l3'].value_counts()
Out[90]:
Palermo              6967
Belgrano             3412
Recoleta             3267
Tigre                1397
Puerto Madero        1395
                     ... 
Villa Lugano            2
Ezeiza                  2
General Rodríguez       1
Presidente Perón        1
San Miguel              1
Name: l3, Length: 74, dtype: int64

Observación: Los barrios con mayor cantidad de propiedades de precio por área mas costoso son Palermo, Belgrano, Recoleta, Tigre y puerto madero.

In [74]:
df_CA.describe(include='object').loc[:,('l2','l3','property_type')]
Out[74]:
l2 l3 property_type
count 28605 28605 28605
unique 4 74 8
top Capital Federal Palermo Departamento
freq 24983 6967 27583
In [75]:
df_CA['property_type'].value_counts()
Out[75]:
Departamento       27583
PH                   750
Casa                 180
Oficina               67
Otro                  20
Lote                   3
Depósito               1
Local comercial        1
Name: property_type, dtype: int64

Observación: Las propiedades de área mas costosa, se localizan en las 4 zonas de Bienos Aires, en 74 barrios y se trata de 8 tipos propiedades diferentes, excluyendo las casas de campo y las cocheras

In [76]:
round(df_CA.describe(include='float64').loc[:,('rooms','bedrooms','bathrooms','surface_total','surface_covered')])
Out[76]:
rooms bedrooms bathrooms surface_total surface_covered
count 28605.0 28605.0 28605.0 28605.0 28605.0
mean 3.0 2.0 2.0 94.0 89.0
std 1.0 1.0 1.0 75.0 173.0
min 1.0 0.0 1.0 10.0 2.0
25% 2.0 1.0 1.0 50.0 45.0
50% 3.0 2.0 1.0 71.0 65.0
75% 4.0 3.0 2.0 110.0 100.0
max 14.0 8.0 14.0 1150.0 13402.0

Observación: El percentil 75% de las propiedades de área mas costosa, tienen menos de 4 salones, 3 habitaciones, 2 baños, una superficie total de 110 unidades y una superfice cubierta de 100 unidades, por su parte el cuarto cuantil de las propiedades de area mas costosas tienen entre 5 y 14 salones, entre 4 y 8 habitaciones, entre 3 y 14 baños, entre 111 y 1150 unidades de area total y entre 101 y 13402 unidades de area cubierta.

A continuación se realiza graficamente la descripción de estas características, por tipo de propiedad y zona a la que pertenecen.

In [77]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='surface_total', data=df_CA, hue='l2')
plt.xticks(rotation=45)
plt.title("Tipo de propiedad vs. Area Total", fontsize=20, verticalalignment='bottom')
Out[77]:
Text(0.5, 1.0, 'Tipo de propiedad vs. Area Total')
In [78]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='log_S_covered', data=df_CA, hue='l2')
plt.xticks(rotation=45)
plt.title("Tipo de propiedad vs. Area Costruida", fontsize=20, verticalalignment='bottom')
Out[78]:
Text(0.5, 1.0, 'Tipo de propiedad vs. Area Costruida')
In [79]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='rooms', data=df_CA, hue='l2')
plt.xticks(rotation=45)
plt.title("Tipo de propiedad vs. Salones", fontsize=20, verticalalignment='bottom')
Out[79]:
Text(0.5, 1.0, 'Tipo de propiedad vs. Salones')
In [80]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='bedrooms', data=df_CA, hue='l2')
plt.xticks(rotation=90)
plt.title("Tipo de propiedad vs. habitaciones", fontsize=20, verticalalignment='bottom')
Out[80]:
Text(0.5, 1.0, 'Tipo de propiedad vs. habitaciones')
In [81]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='bathrooms', data=df_CA, hue='l2')
plt.xticks(rotation=90)
plt.title("Tipo de propiedad vs. Baños", fontsize=20, verticalalignment='bottom')
Out[81]:
Text(0.5, 1.0, 'Tipo de propiedad vs. Baños')
In [93]:
plt.rcParams.update({'font.size': 15})
plt.figure(figsize=(20,8))
sns.boxplot(x='property_type', y='log_Price_per_S_total', data=df_CA, hue='l2')
plt.xticks(rotation=90)
plt.title("Tipo de propiedad vs. Precio", fontsize=20, verticalalignment='bottom')
Out[93]:
Text(0.5, 1.0, 'Tipo de propiedad vs. Precio')

4. Arquitectura de datos

La Arquitectura propuesta se puede apreciar en git: https://dlesmes.github.io/Antivirus/4_Diagram_Architecture.pdf